Skip to content

Fix pixel breath negative TIV#369

Merged
psomhorst merged 26 commits intodevelopfrom
fix_pixel_breath_negative_tiv
May 12, 2025
Merged

Fix pixel breath negative TIV#369
psomhorst merged 26 commits intodevelopfrom
fix_pixel_breath_negative_tiv

Conversation

@psomhorst
Copy link
Copy Markdown
Contributor

This should fix the issue where pixels that have a negative absolute value are ignored during pixel breath detection, resulting in them being excluded in TIV maps.

It also adds a boolean to choose to allow pixels with negative amplitude. There are now two options:

  • A pixel that has a decreasing amplitude during an externally (e.g., globally) defined inspiration is a pixel with a negative amplitude (allow_negative_amplitude = True).
  • Each pixel is defined as having a positive amplitude, but some can have a large phase shift (allow_negative_amplitude = False).

Which definition you use will depend on the use case.

Pixels are now skipped when they have no amplitude, i.e. when the standard deviation equals 0.
@psomhorst
Copy link
Copy Markdown
Contributor Author

@JulietteFrancovich I think this is what we discussed. For me, this fixes the issue where some/a lot of pixels do not show up on the TIV map. We should discuss which definition we use when creating TIV maps. I think there are two options:

  1. not allowing negative pixels (potentially creating pixels with large phase shifts) or
  2. taking the absolute value for the TIV map

What do you think?

@psomhorst psomhorst changed the base branch from main to develop April 30, 2025 12:43
@psomhorst
Copy link
Copy Markdown
Contributor Author

psomhorst commented May 4, 2025

As discussed, I updated the code.

  • allow_negative_amplitude is now True by default.
  • breath_detection was added as argument, that receives an actual BreathDetection object. The same for PixelBreath (when initializing TIV) and EELI.
  • breath_detection_kwargs is deprecated. It can still be used, but you get a warning. It cannot be used together with breath_detection (will result in a TypeError).

@psomhorst psomhorst marked this pull request as ready for review May 4, 2025 16:22
@psomhorst
Copy link
Copy Markdown
Contributor Author

@JulietteFrancovich I updated the code. Is there a way for you to test it with some of your existing processes, so that we can properly test whether the DeprecationWarning works as intended?

Comment on lines +177 to +202
if self.allow_negative_amplitude and mean_tiv < 0:
start_func, middle_func = np.argmax, np.argmin
lagged_indices_breath_middles = indices_breath_middles
else:
start_func, middle_func = np.argmin, np.argmax

cd = np.copy(continuous_data.values)
cd -= np.nanmean(cd)
pi = np.copy(pixel_impedance[:, row, col])
pi -= np.nanmean(pixel_impedance[:, row, col])

if self.correct_for_phase_shift:
# search for maximum cross correlation within MAX_XCORR_LAG times the average
# duration of a breath
xcorr = signal.correlate(cd, pi, mode="same")
max_lag = MAX_XCORR_LAG * np.mean(np.diff(indices_breath_middles))
lag_range = (lags > -max_lag) & (lags < max_lag)
# TODO: if this does not work, implement robust peak detection
lag = lags[lag_range][np.argmax(xcorr[lag_range])]
# positive lag: pixel inflates later than summed

# shift search area
lagged_indices_breath_middles = indices_breath_middles - lag
lagged_indices_breath_middles = lagged_indices_breath_middles[
(lagged_indices_breath_middles >= 0) & (lagged_indices_breath_middles < len(cd))
]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to have allow_negative_amplitude and correct_phase_shift as separate arguments? Allowing negative amplitude without phase shift correction may lead to misinterpretation of out-of-phase signals (as this is the reason for implementing this, right?)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I think you're correct. I was struggling with that last night but didn't come up with a good solution. I think it's better to have a some sort of 'phase_correction_mode' argument with three options, resulting in setting internal boolean variables:

  • "negative amplitude" -> allow_negative_amplidude = True (default)
  • "correct phase shift" -> allow_negative_amplitude = False, correct_phase_shift=True
  • "none"/None -> allow_negative_amplitude = False, correct_phase_shift=False

Would that make sense? Do you have better suggestions for the mode names?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reading your question: yes, I do think it makes sense to have the 'none' option above. This is the old behaviour, and
it works in at least some cases. If it's not necessary to overcomplicate, why should we?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes to me it does make sense to have the none option but I think there shouldn't be an option to have allow_negative_amplitude = True with correct_phase_shift=False, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't an option where allow_negative_amplitude = True with correct_phase_shift=False, because correct_phase_shift is only used when allow_negative_amplitude == False. I have replaced the boolean arguments with a mode argument.
Can you check whether their function is clear from the documentation?

@psomhorst psomhorst merged commit 639d76b into develop May 12, 2025
3 checks passed
@psomhorst psomhorst deleted the fix_pixel_breath_negative_tiv branch May 26, 2025 12:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants